﻿using System;

namespace SkeinLibManaged
{       
    public static partial class ThreeFish
    {
        #region "Core Utilities Specific for 512"
        private static void MIX512(ref UInt64 X1, ref UInt64 Y1, RC512 N1,
                            ref UInt64 X2, ref UInt64 Y2, RC512 N2,
                            ref UInt64 X3, ref UInt64 Y3, RC512 N3,
                            ref UInt64 X4, ref UInt64 Y4, RC512 N4)
        {   X1 += Y1;   ROTL64(ref Y1, (byte)(N1));     Y1 ^= X1;
            X2 += Y2;   ROTL64(ref Y2, (byte)(N2));     Y2 ^= X2;
            X3 += Y3;   ROTL64(ref Y3, (byte)(N3));     Y3 ^= X3;
            X4 += Y4;   ROTL64(ref Y4, (byte)(N4));     Y4 ^= X4;
        }

        private static void INVMIX512(ref UInt64 X1, ref UInt64 Y1, RC512 N1,
                               ref UInt64 X2, ref UInt64 Y2, RC512 N2,
                               ref UInt64 X3, ref UInt64 Y3, RC512 N3,
                               ref UInt64 X4, ref UInt64 Y4, RC512 N4)
        {   Y1 ^= X1;   ROTR64(ref Y1, (byte)(N1));     X1 -= Y1;
            Y2 ^= X2;   ROTR64(ref Y2, (byte)(N2));     X2 -= Y2;
            Y3 ^= X3;   ROTR64(ref Y3, (byte)(N3));     X3 -= Y3;
            Y4 ^= X4;   ROTR64(ref Y4, (byte)(N4));     X4 -= Y4;
        }

        private static void KEYI512(UInt64[] k, UInt64[] t, System.Byte round, UInt64[] b)
        {   b[0] += k[(round + 0) % 9];
            b[1] += k[(round + 1) % 9];
            b[2] += k[(round + 2) % 9];
            b[3] += k[(round + 3) % 9];
            b[4] += k[(round + 4) % 9];
            b[5] += k[(round + 5) % 9] + t[(round + 0) % 3];
            b[6] += k[(round + 6) % 9] + t[(round + 1) % 3];
            b[7] += k[(round + 7) % 9] + round;
        }

        private static void INVKEYI512(UInt64[] k, UInt64[] t, System.Byte round, UInt64[] b)
        {   b[0] -= (k[(round + 0) % 9]);
            b[1] -= (k[(round + 1) % 9]);
            b[2] -= (k[(round + 2) % 9]);
            b[3] -= (k[(round + 3) % 9]);
            b[4] -= (k[(round + 4) % 9]);
            b[5] -= (k[(round + 5) % 9] + t[(round + 0) % 3]);
            b[6] -= (k[(round + 6) % 9] + t[(round + 1) % 3]);
            b[7] -= (k[(round + 7) % 9] + round);
        }
        #endregion

        /// <summary>
        /// Transform eight 64-bit words according to ThreeFish algorithm.
        /// </summary>
        /// <param name="input">The eight 64-bit words to be transformed.</param>
        /// <param name="key">The eight 64-bit key words used during the transformation.</param>
        /// <param name="tweak">The two 64-bit tweak words used in conjunction with the key.</param>
        /// <param name="feed">False for encryption, true for hashing mechanism used by Skein.</param>
        /// <returns>Array of eight 64-bit tranformed words.</returns>
        public static UInt64[] ThreeFish512Words(
                    UInt64[] input, UInt64[] key, UInt64[] tweak, System.Boolean feed)
        {   
            //Error checking!
            if (input == null)
                throw new System.ArgumentNullException("ThreeFish512Words: Input cannot be null!");
            if (key == null)
                throw new System.ArgumentNullException("ThreeFish512Words: Key cannot be null!");
            if (tweak == null)
                throw new System.ArgumentNullException("ThreeFish512Words: Tweak cannot be null!");
            if (input.Length != 8)
                throw new System.ArgumentException("ThreeFish512Words: Incorrect length for input word array.");
            if (key.Length != 8)
                throw new System.ArgumentException("ThreeFish512Words: Incorrect length for key word array.");
            if (tweak.Length != 2)
                throw new System.ArgumentException("ThreeFish512Words: Incorrect length for tweak word array.");
            UInt64[] buf = {input[0], input[1], input[2], input[3],
                            input[4], input[5], input[6], input[7]};
            UInt64[] locKey = {key[0], key[1], key[2], key[3], 
                               key[4], key[5], key[6], key[7], 
                               UNIQUE_K(key)};
            UInt64[] locTweak = {tweak[0], tweak[1], (tweak[0] ^ tweak[1])};
            UInt64[] locFeed = null;
            if (feed == true)
            {   locFeed = new UInt64[] { input[0], input[1], input[2], input[3], input[4], input[5], input[6], input[7] };
            };
            
            KEYI512(locKey, locTweak, 0, buf);
            MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 1, buf);
            MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            KEYI512(locKey, locTweak, 2, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 3, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);	
            KEYI512(locKey, locTweak, 4, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 5, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);	
            KEYI512(locKey, locTweak, 6, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 7, buf);	
	        MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);	
            KEYI512(locKey, locTweak, 8, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 9, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);	
            KEYI512(locKey, locTweak, 10, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 11, buf);	
	        MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);	
            KEYI512(locKey, locTweak, 12, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 13, buf);	
	        MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);	
            KEYI512(locKey, locTweak, 14, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 15, buf);	
	        MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);	
            KEYI512(locKey, locTweak, 16, buf);
	        MIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            MIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            MIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            MIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);	
            KEYI512(locKey, locTweak, 17, buf);	
	        MIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            MIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            MIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            MIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);	
            KEYI512(locKey, locTweak, 18, buf);
         
	        for (byte i = 0; i < 8; i++)
            {   if (feed == true) buf[i] ^= locFeed[i];
            }
            return buf;
        }

        /// <summary>
        /// If the ThreeFish512Words transformation was done with "feed" = false, the words can be "untransformed" with the
        /// SAME key AND tweak using this function.
        /// </summary>
        /// <param name="input">The eight 64-bit key words to be recovered.</param>
        /// <param name="key">The eight 64-bit key words used during the transformation.</param>
        /// <param name="tweak">The two 64-bit tweak words used in conjunction with the key.</param>
        /// <returns>Array of eight 64-bit untranformed words.</returns>
        public static UInt64[] InvThreeFish512Words(
                    UInt64[] input, UInt64[] key, UInt64[] tweak)
        {   
            //Error checking!
            if (input == null)
                throw new System.ArgumentNullException("InvThreeFish512Words: Input cannot be null!");
            if (key == null)
                throw new System.ArgumentNullException("InvThreeFish512Words: Key cannot be null!");
            if (tweak == null)
                throw new System.ArgumentNullException("InvThreeFish512Words: Tweak cannot be null!");
            if (input.Length != 8)
                throw new System.ArgumentException("InvThreeFish512Words: Incorrect length for input word array.");
            if (key.Length != 8)
                throw new System.ArgumentException("InvThreeFish512Words: Incorrect length for key word array.");
            if (tweak.Length != 2)
                throw new System.ArgumentException("InvThreeFish512Words: Incorrect length for tweak word array.");
            UInt64[] buf = { input[0], input[1], input[2], input[3],
                             input[4], input[5], input[6], input[7] };
            UInt64[] locKey = { key[0], key[1], key[2], key[3],
                                key[4], key[5], key[6], key[7],
                                UNIQUE_K(key) };
            UInt64[] locTweak = { tweak[0], tweak[1], (tweak[0] ^ tweak[1]) };

            INVKEYI512(locKey, locTweak, 18, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 17, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 16, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 15, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 14, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 13, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 12, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 11, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 10, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 9, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 8, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 7, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 6, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 5, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 4, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 3, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 2, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R7A, ref buf[0], ref buf[7], RC512.R7B, ref buf[2], ref buf[5], RC512.R7C, ref buf[4], ref buf[3], RC512.R7D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R6A, ref buf[6], ref buf[3], RC512.R6B, ref buf[0], ref buf[5], RC512.R6C, ref buf[2], ref buf[7], RC512.R6D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R5A, ref buf[4], ref buf[7], RC512.R5B, ref buf[6], ref buf[5], RC512.R5C, ref buf[0], ref buf[3], RC512.R5D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R4A, ref buf[2], ref buf[3], RC512.R4B, ref buf[4], ref buf[5], RC512.R4C, ref buf[6], ref buf[7], RC512.R4D);
            INVKEYI512(locKey, locTweak, 1, buf);
            INVMIX512(ref buf[6], ref buf[1], RC512.R3A, ref buf[0], ref buf[7], RC512.R3B, ref buf[2], ref buf[5], RC512.R3C, ref buf[4], ref buf[3], RC512.R3D);
            INVMIX512(ref buf[4], ref buf[1], RC512.R2A, ref buf[6], ref buf[3], RC512.R2B, ref buf[0], ref buf[5], RC512.R2C, ref buf[2], ref buf[7], RC512.R2D);
            INVMIX512(ref buf[2], ref buf[1], RC512.R1A, ref buf[4], ref buf[7], RC512.R1B, ref buf[6], ref buf[5], RC512.R1C, ref buf[0], ref buf[3], RC512.R1D);
            INVMIX512(ref buf[0], ref buf[1], RC512.R0A, ref buf[2], ref buf[3], RC512.R0B, ref buf[4], ref buf[5], RC512.R0C, ref buf[6], ref buf[7], RC512.R0D);
            INVKEYI512(locKey, locTweak, 0, buf);

            return buf;
        }
    }
}
